use actix_web::{web, HttpResponse, Responder};
use serde::Deserialize;
use std::sync::Arc;
use tokio::sync::Mutex;

use crate::modules::sftp::service::{SftpService, SftpCredentials, FileEntry};

#[derive(Deserialize)]
pub struct PathQuery { pub session_id: usize, pub path: Option<String> }

#[derive(Deserialize)]
pub struct CreateSessionPayload { pub hostname: String, pub port: u16, pub username: String, pub password: String }

#[derive(Deserialize)]
pub struct DeletePayload { pub session_id: usize, pub path: String }

#[derive(Deserialize)]
pub struct RenamePayload { pub session_id: usize, pub path: String, pub new_name: String }

#[derive(Deserialize)]
pub struct UploadPayload { pub session_id: usize, pub path: String, pub filename: String, pub content_base64: String }

pub async fn create_session(
    service: web::Data<Arc<Mutex<SftpService>>>,
    payload: web::Json<CreateSessionPayload>,
) -> impl Responder {
    let creds = SftpCredentials {
        hostname: payload.hostname.clone(),
        port: payload.port,
        username: payload.username.clone(),
        password: payload.password.clone(),
    };
    let service = service.get_ref().clone();
    match service.lock().await.create_session(creds).await {
        Ok(id) => HttpResponse::Ok().json(serde_json::json!({"session_id": id})),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

pub async fn list_dir(
    service: web::Data<Arc<Mutex<SftpService>>>,
    q: web::Query<PathQuery>,
) -> impl Responder {
    let path = q.path.clone().unwrap_or("/".to_string());
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(q.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.list(&path) {
        Ok(entries) => HttpResponse::Ok().json(entries),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

pub async fn read_file(
    service: web::Data<Arc<Mutex<SftpService>>>,
    q: web::Query<PathQuery>,
) -> impl Responder {
    let path = q.path.clone().unwrap_or("/".to_string());
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(q.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.read_text(&path) {
        Ok(text) => HttpResponse::Ok().json(serde_json::json!({"content": text})),
        Err(e) => HttpResponse::UnsupportedMediaType().body(e.to_string()),
    }
}

pub async fn delete_file(
    service: web::Data<Arc<Mutex<SftpService>>>,
    payload: web::Json<DeletePayload>,
) -> impl Responder {
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(payload.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.delete(&payload.path) {
        Ok(()) => HttpResponse::Ok().body("OK"),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

pub async fn rename_file(
    service: web::Data<Arc<Mutex<SftpService>>>,
    payload: web::Json<RenamePayload>,
) -> impl Responder {
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(payload.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.rename(&payload.path, &payload.new_name) {
        Ok(()) => HttpResponse::Ok().body("OK"),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

pub async fn upload_file(
    service: web::Data<Arc<Mutex<SftpService>>>,
    payload: web::Json<UploadPayload>,
) -> impl Responder {
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(payload.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.upload_base64(&payload.path, &payload.filename, &payload.content_base64) {
        Ok(()) => HttpResponse::Ok().body("OK"),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

pub async fn download_file(
    service: web::Data<Arc<Mutex<SftpService>>>,
    q: web::Query<PathQuery>,
) -> impl Responder {
    let path = q.path.clone().unwrap_or("/".to_string());
    let service = service.get_ref().clone();
    let guard = match service.lock().await.get_session(q.session_id).await { Ok(g) => g, Err(e) => return HttpResponse::BadRequest().body(e.to_string()) };
    match guard.download(&path) {
        Ok(bytes) => {
            let fname = std::path::Path::new(&path).file_name().and_then(|s| s.to_str()).unwrap_or("download");
            HttpResponse::Ok()
                .append_header(("Content-Type", "application/octet-stream"))
                .append_header(("Content-Disposition", format!("attachment; filename=\"{}\"", fname)))
                .body(bytes)
        }
        Err(e) => HttpResponse::NotFound().body(e.to_string()),
    }
}